/**
 * 
 */
package org.braketester.view;

import java.awt.Color;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.ArrayList;
import java.util.LinkedList;

import javax.swing.BorderFactory;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;

import org.braketester.entry.AbstractApplyer;
import org.braketester.entry.ExceptionResolver;
import org.braketester.entry.FramesThread;
import org.braketester.model.ChannelComboBoxItem;
import org.braketester.model.LTR212DeviceSingletonCeeper;
import org.braketester.model.LTR212Preferences;
import org.braketester.model.LTRLoader;
import org.braketester.model.PreferencesLoader;
import org.extdev.device.ltr.LTRChannel;
import org.extdev.device.ltr.LTRCrate;
import org.extdev.device.ltr.LTRCrateConteiner;
import org.extdev.libraries.ltr.LTRAPIConstants;
import org.extdev.libraries.ltr.LTRException;

/**
 * @author LAP
 *
 */
public class JLTRPanel extends JPanel {
	private static final long serialVersionUID = 8599535443030502547L;
	private JLabel crates;
	private JComboBox<LTRCrate> cratesComboBox;
	private JLabel modules;
	private JComboBox<Integer> modulesComboBox;
	private JLabel acqMode;
	private JComboBox<String> acqModeComboBox;
	private JLabel channels;
	private JComboBox<ChannelComboBoxItem> channelsComboBox;
	private JPanel channelPreferences;
	private JCheckBox activeChannel;
	private JLabel scale;
	private JComboBox<String> scaleComboBox;
	private GridBagConstraints outerConstraints;
	private LTR212Applyer applyer;
	
	public JLTRPanel(LTRCrateConteiner conteiner) {
		super();
		createGUI(conteiner);
		applyer = new LTR212Applyer(getLTRPreferences());
	}

	/**
	 * @return the cratesComboBox
	 */
	public JComboBox<LTRCrate> getCratesComboBox() {
		return cratesComboBox;
	}

	/**
	 * @return the modulesComboBox
	 */
	public JComboBox<Integer> getModulesComboBox() {
		return modulesComboBox;
	}

	/**
	 * @return the acqModeComboBox
	 */
	public JComboBox<String> getAcqModeComboBox() {
		return acqModeComboBox;
	}

	/**
	 * @return the channelsComboBox
	 */
	public JComboBox<ChannelComboBoxItem> getChannelsComboBox() {
		return channelsComboBox;
	}

	/**
	 * @return the activeChannel
	 */
	public JCheckBox getActiveChannel() {
		return activeChannel;
	}

	/**
	 * @return the scaleComboBox
	 */
	public JComboBox<String> getScaleComboBox() {
		return scaleComboBox;
	}
	
	private LTR212Preferences getLTRPreferences() {
		LTR212Preferences preferences = new LTR212Preferences();
		preferences.setCrate((LTRCrate) cratesComboBox.getSelectedItem());
		ArrayList<ChannelComboBoxItem> arrayList = new ArrayList<ChannelComboBoxItem>();
		for(int i = 0; i < channelsComboBox.getItemCount(); i++)
			arrayList.add(channelsComboBox.getItemAt(i));
		preferences.setChannels(arrayList);
		preferences.setAcqMode(acqModeComboBox.getSelectedIndex());
		if (modulesComboBox.getSelectedItem() != null)
			preferences.setModuleNo((int) modulesComboBox.getSelectedItem());
		return preferences;
	}
	
	private void setLTRPreferences(LTR212Preferences preferences) {
		int crateIndex = -1;
		int moduleIndex = -1;
		for(LTRCrate crate : LTRLoader.getInstance().getContainer().getCrates()) {
			if (preferences.getCrate().getSerialNumberOfCrate().equals(crate.getSerialNumberOfCrate())) {
				int i = 1;
				for(Short j : crate.getModules()) {
					if (j != LTRAPIConstants.MID_LTR212)
						i++;
					else {
						if (i == preferences.getModuleNo()) {
							moduleIndex++;
							break;
						}
					}
				}
				break;
			}
			else crateIndex++;
		}
		if (moduleIndex != -1) {
			cratesComboBox.setSelectedIndex(crateIndex + 1);
			modulesComboBox.setSelectedIndex(moduleIndex);
			acqModeComboBox.setSelectedIndex(preferences.getAcqMode());
			channelsComboBox.removeAllItems();
			for(ChannelComboBoxItem item : preferences.getChannels())
				channelsComboBox.addItem(item);
		}
		else {
			cratesComboBox.setSelectedIndex(0);
			modulesComboBox.setSelectedIndex(0);
			acqModeComboBox.setSelectedIndex(1);
		}
	}

	private void createGUI(LTRCrateConteiner conteiner) {
		setLayout(new GridBagLayout());
		outerConstraints = new GridBagConstraints();
		outerConstraints.insets.top = 2;
		outerConstraints.gridx = 0;
		outerConstraints.gridy = 0;
		outerConstraints.anchor = GridBagConstraints.NORTHWEST;
		createCratesLine(conteiner);
		createModulesLine();
		createACQModeLine();
		createChannelsLine();
		outerConstraints.gridx = 0;
		outerConstraints.gridy++;
		outerConstraints.gridwidth = 2;
		outerConstraints.fill = GridBagConstraints.BOTH;
		outerConstraints.weighty = 1;
		outerConstraints.insets.left = 0;
		createChannelPreferencesPanel();
	}

	private void createChannelPreferencesPanel() {
		channelPreferences = new JPanel();
		channelPreferences.setBorder(BorderFactory.createLineBorder(Color.BLACK, 2));
		add(channelPreferences, outerConstraints);
		GridBagConstraints gbc1 = new GridBagConstraints();
		channelPreferences.setLayout(new GridBagLayout());
		gbc1.insets.top = 2;
		gbc1.gridx = 0;
		gbc1.gridy = 0;
		gbc1.anchor = GridBagConstraints.WEST;
		gbc1.gridwidth = 2;
		activeChannel = new JCheckBox("Active");
		activeChannel.addItemListener(new ActiveChannelItemListener());
		activeChannel.addItemListener(new AddPreferencesToApplyerItemListener());
		channelPreferences.add(activeChannel, gbc1);
		gbc1.gridy++;
		gbc1.gridwidth = 1;
		gbc1.weighty = 1;
		scale = new JLabel("Scale");
		channelPreferences.add(scale, gbc1);
		gbc1.gridx++;
		gbc1.weightx = 1;
		gbc1.fill = GridBagConstraints.HORIZONTAL;
		scaleComboBox = new JComboBox<String>(new String[] {
				 "-10 mV/+10mV",
				 "-20 mV/+20mV",
				 "-40 mV/+40mV",
				 "-80 mV/+80mV",
				 "0 mV/+10mV",
				 "0 mV/+20mV",
				 "0 mV/+40mV",
				 "0 mV/+80mV"});
		scaleComboBox.addItemListener(new ScaleComboBoxItemListener());
		scaleComboBox.addItemListener(new AddPreferencesToApplyerItemListener());
		scaleComboBox.setEnabled(false);
		channelPreferences.add(scaleComboBox, gbc1);
	}

	private void createChannelsLine() {
		rightComponentConstraintsSet();
		channels = new JLabel("Channels");
		add(channels, outerConstraints);
		leftComponentConstraintsSet();
		channelsComboBox = new JComboBox<ChannelComboBoxItem>();
		channelsComboBox.addItemListener(new ChannelsComboBoxItemListener());
		add(channelsComboBox, outerConstraints);
	}

	private void createACQModeLine() {
		rightComponentConstraintsSet();
		acqMode = new JLabel("ACQ Mode");
		add(acqMode, outerConstraints);
		leftComponentConstraintsSet();
		acqModeComboBox = new JComboBox<String>(new String[] {
			"ACQ_MODE_MEDIUM_PRECISION", 
			"ACQ_MODE_HIGH_PRECISION", 
			"ACQ_MODE_8CH_HIGH_PRECISION"});
		add(acqModeComboBox, outerConstraints);
		acqModeComboBox.setSelectedIndex(-1);
		acqModeComboBox.addItemListener(new ACQModeComboBoxItemListener());
		acqModeComboBox.addItemListener(new AddPreferencesToApplyerItemListener());
	}

	private void createModulesLine() {
		rightComponentConstraintsSet();
		modules = new JLabel("Modules");
		add(modules, outerConstraints);
		leftComponentConstraintsSet();
		modulesComboBox = new JComboBox<Integer>();
		modulesComboBox.addItemListener(new AddPreferencesToApplyerItemListener());
		add(modulesComboBox, outerConstraints);
	}

	private void createCratesLine(LTRCrateConteiner conteiner) {
		crates = new JLabel("Crates");
		add(crates, outerConstraints);
		leftComponentConstraintsSet();
		cratesComboBox = new JComboBox<LTRCrate>();
		for(LTRCrate crate : conteiner.getCrates()) cratesComboBox.addItem(crate);
		cratesComboBox.setSelectedIndex(-1);
		cratesComboBox.addItemListener(new CratesComboBoxItemListener());
		cratesComboBox.addItemListener(new AddPreferencesToApplyerItemListener());
		add(cratesComboBox, outerConstraints);
	}

	private void rightComponentConstraintsSet() {
		outerConstraints.gridx = 0;
		outerConstraints.gridy++;
		outerConstraints.fill = GridBagConstraints.NONE;
		outerConstraints.weightx = 0;
		outerConstraints.insets.left = 0;
	}

	private void leftComponentConstraintsSet() {
		outerConstraints.gridx++;
		outerConstraints.weightx = 1;
		outerConstraints.fill = GridBagConstraints.HORIZONTAL;
		outerConstraints.insets.left = 5;
	}
	
	private void fillChannelsComboBox(String acqMode) {
		channelsComboBox.removeAllItems();
		switch (acqMode) {
		case "ACQ_MODE_MEDIUM_PRECISION": case "ACQ_MODE_HIGH_PRECISION": 
			for(int i = 0; i < 4; i++) channelsComboBox.addItem(new ChannelComboBoxItem(new LTRChannel(i + 1)));
			break;
		case "ACQ_MODE_8CH_HIGH_PRECISION": 
			for(int i = 0; i < 8; i++) channelsComboBox.addItem(new ChannelComboBoxItem(new LTRChannel(i + 1)));
			break;
		}
	}
	
	private void fillModulesComboBox(LinkedList<Short> modules) {
		modulesComboBox.removeAllItems();
		int i = 1;
		for(Short module : modules) 
			if (module != LTRAPIConstants.MID_LTR212) i++;
			else modulesComboBox.addItem(i);
		modulesComboBox.setSelectedIndex(-1);
	}
	
	private class ACQModeComboBoxItemListener implements ItemListener {
		@Override
		public void itemStateChanged(ItemEvent e) {
			fillChannelsComboBox(e.getItem().toString());
		}
	}
	
	private class CratesComboBoxItemListener implements ItemListener {
		@Override
		public void itemStateChanged(ItemEvent e) {
			LTRCrate crate = (LTRCrate) e.getItem();
			fillModulesComboBox(crate.getModules());
			acqModeComboBox.setSelectedIndex(-1);
		}
	}
	
	private class ActiveChannelItemListener implements ItemListener {
		@Override
		public void itemStateChanged(ItemEvent e) {
			boolean state = e.getStateChange() == ItemEvent.SELECTED;
				channelsComboBox.getItemAt(channelsComboBox.getSelectedIndex()).setActive(state);
				scaleComboBox.setEnabled(state);
		}
	}
	
	private class ScaleComboBoxItemListener implements ItemListener {
		@Override
		public void itemStateChanged(ItemEvent e) {
			channelsComboBox.getItemAt(channelsComboBox.getSelectedIndex()).getChannel().setScale(
					scaleComboBox.getSelectedIndex());
		}
	}
	
	private class ChannelsComboBoxItemListener implements ItemListener {
		@Override
		public void itemStateChanged(ItemEvent e) {
			ChannelComboBoxItem item = (ChannelComboBoxItem)e.getItem();
			activeChannel.setSelected(item.isActive());
			scaleComboBox.setSelectedIndex(item.getChannel().getScale());
		}
	}
	
	private class AddPreferencesToApplyerItemListener implements ItemListener {
		@Override
		public void itemStateChanged(ItemEvent e) {
			applyer.setPreferences(getLTRPreferences());
		}
	}
	
	private class LTR212Applyer extends AbstractApplyer<LTR212Preferences> {

		@Override
		public void apply() {
			if (oldPreferences.getCrate() == null 
					|| !oldPreferences.getCrate().equals(changedPreferences.getCrate())
					|| oldPreferences.getModuleNo() != changedPreferences.getModuleNo())
				LTR212DeviceSingletonCeeper.getInstance().resetConnection(changedPreferences);
			if (oldPreferences.getAcqMode() != changedPreferences.getAcqMode()) {
				LTR212DeviceSingletonCeeper.getInstance().getDevice().setACQMode(
						changedPreferences.getAcqMode());
			}
			if (oldPreferences.getChannels() == null 
					|| !oldPreferences.getChannels().equals(changedPreferences.getChannels())) {
				LTR212DeviceSingletonCeeper.getInstance().getDevice().removeAllChannels();
				for(int i = 0; i < changedPreferences.getChannels().size(); i++) {
					if (changedPreferences.getChannels().get(i).isActive())
						LTR212DeviceSingletonCeeper.getInstance().getDevice().setChannels(
								changedPreferences.getChannels().get(i).getChannel());
					}
				}
			tryConnect();
			trySaveSettings();
			oldPreferences = changedPreferences;
			setLTRPreferences(oldPreferences);
			PreferencesLoader.getInstance().savePreferences(oldPreferences);
			changedPreferences = null;
		}
		
		private void tryConnect() {
			try {
				LTR212DeviceSingletonCeeper.getInstance().getDevice().getConnection().connect();
			} catch (Exception e) {
				ExceptionResolver.getInstance().logException(e);
				ExceptionResolver.getInstance().showException(e);
				System.exit(((LTRException)e).getExceptionNumber());
			}
		}

		private void trySaveSettings() {
			try {
				LTR212DeviceSingletonCeeper.getInstance().getDevice().saveSettings();
			} catch (LTRException e) {
				ExceptionResolver.getInstance().logException(e);
				ExceptionResolver.getInstance().showException(e);
			}
		}
		
		public LTR212Applyer(LTR212Preferences preferences) {
			super(preferences);
		}

		@Override
		public void cancel() {
			FramesThread.getInstance().getOptionsFrame().getLTRPanel().setLTRPreferences(oldPreferences);
		}
	}
	
	public AbstractApplyer<LTR212Preferences> getApplyer() {
		return applyer;
	}
}
